package common

import (
	"github.com/golang/protobuf/proto"
	"hundsun.com/hsl/hschain/common/log"
	"hundsun.com/hsl/hschain/common/util/pb"
	execcomm "hundsun.com/hsl/hschain/executor/common"
	"hundsun.com/hsl/hschain/protos/execute"
	protosledger "hundsun.com/hsl/hschain/protos/ledger"
	"hundsun.com/hsl/hschain/protos/manage"
)

// UpdateFlowStatus 更新流程status
func UpdateFlowStatus(ctx *execcomm.ExecContent, gtx *execute.GroupedTransaction, flowStatus *manage.ManageFlowStatus, ty uint32) error {
	statusKV, err := StatusKV(flowStatus)
	if err != nil {
		gtx.TxExecRst.Receipt.TxStatus = protosledger.TransactionReceipt_FAILED_OTHER_REASON
		return err
	}

	ctx.GetStateHandle().SetLocal(statusKV.Key, statusKV.Value)
	gtx.TxExecRst.Kvs = append(gtx.TxExecRst.Kvs, statusKV)
	gtx.TxExecRst.Receipt.Logs = append(gtx.TxExecRst.Receipt.Logs, &protosledger.Log{Ty: ty, Data: statusKV.Value})

	nmStatusRWStatus := &execute.RWStatus{Key: statusKV.GetKey(), RwStatus: execute.RwStatusType_WriteStatus}
	gtx.VarsRWStatus = append(gtx.VarsRWStatus, nmStatusRWStatus)

	return nil
}

// UpdateFlowID 更新流程ID
func UpdateFlowID(ctx *execcomm.ExecContent, gtx *execute.GroupedTransaction, flowID uint32) error {
	kv, err := IDKV(flowID)
	if err != nil {
		gtx.TxExecRst.Receipt.TxStatus = protosledger.TransactionReceipt_FAILED_OTHER_REASON
		return err
	}
	ctx.GetStateHandle().SetLocal(kv.Key, kv.Value)
	gtx.TxExecRst.Kvs = append(gtx.TxExecRst.Kvs, kv)
	gtx.TxExecRst.Receipt.Logs = append(gtx.TxExecRst.Receipt.Logs, &protosledger.Log{Ty: TyLogUpdateManageFlowID, Data: kv.Value})
	nmIDRWStatus := &execute.RWStatus{Key: kv.GetKey(), RwStatus: execute.RwStatusType_WriteStatus}
	gtx.VarsRWStatus = append(gtx.VarsRWStatus, nmIDRWStatus)

	return nil
}

// GetValidators 获取委员会列表
func GetValidators(ctx *execcomm.ExecContent, gtx *execute.GroupedTransaction, flowType manage.ProposalType, bCheckVote bool) (*manage.ManageFlowValidatorInfo, error) {
	var validatorInfo = &manage.ManageFlowValidatorInfo{}
	var validatorKey []byte
	switch flowType {
	case manage.ProposalType_UserGroupProposalType:
		validatorKey = GenGroupInfoKey(manage.UserGroupType_MainBoard)
	case manage.ProposalType_SysParamProposalType:
		validatorKey = GenGroupInfoKey(manage.UserGroupType_SysParamOperator)
		if bCheckVote {
			validatorKey = GenGroupInfoKey(manage.UserGroupType_SysParamBoard)
		}
	case manage.ProposalType_ConsensusUpdateProposalType:
		validatorKey = GenGroupInfoKey(manage.UserGroupType_ConsensusOperator)
		if bCheckVote {
			validatorKey = GenGroupInfoKey(manage.UserGroupType_ConsensusBoard)
		}
	case manage.ProposalType_CertProposalType:
		validatorKey = GenGroupInfoKey(manage.UserGroupType_CertsOperator)
		if bCheckVote {
			validatorKey = GenGroupInfoKey(manage.UserGroupType_CertsBoard)
		}
	case manage.ProposalType_ParaChainProposalType:
		validatorKey = GenGroupInfoKey(manage.UserGroupType_ParaChainOperator)
		if bCheckVote {
			validatorKey = GenGroupInfoKey(manage.UserGroupType_ParaChainBoard)
		}
	}

	validatorBytes, err := ctx.Get(validatorKey)
	if err != nil && flowType != manage.ProposalType_UserGroupProposalType {
		log.Errorf("get validator info failed, key: %s", validatorKey)
		return nil, err
	}

	if len(validatorBytes) > 0 {
		err = proto.Unmarshal(validatorBytes, validatorInfo)
		if err != nil {
			log.Errorf("unmarshal validator info failed, err: %s", err.Error())
			return nil, err
		}
	} else {
		genesisTransactionPayload := pb.GetGenesisTransactionPayload()
		if genesisTransactionPayload != nil && genesisTransactionPayload.Manager != nil &&
			len(genesisTransactionPayload.Manager.Addrs) > 0 {
			validatorInfo.Validators = append(validatorInfo.Validators, genesisTransactionPayload.Manager.Addrs...)
			kv, err := UserGroupKV(manage.UserGroupType_MainBoard, validatorInfo)
			if err != nil {
				return nil, err
			}
			ctx.GetStateHandle().SetLocal(kv.Key, kv.Value)
		}
	}

	validatorRWStatus := &execute.RWStatus{Key: validatorKey, RwStatus: execute.RwStatusType_ReadStatus}
	gtx.VarsRWStatus = append(gtx.VarsRWStatus, validatorRWStatus)

	return validatorInfo, nil
}
